MODULE_NAME='DTV HR21 IP Driver' (Dev dvDTV_Rcvr, Dev vdvDTV_Rcvr, Char strIPAddress[])
(***********************************************************)
(***********************************************************)
(*  FILE_LAST_MODIFIED_ON: 04/04/2006  AT: 11:33:16        *)
(***********************************************************)
(* System Type : NetLinx                                   *)
(***********************************************************)
(* REV HISTORY:                                            *)
(***********************************************************)
(*
    $History: $
*)
#DEFINE DEBUG
(***********************************************************)
(*               CONSTANT DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_CONSTANT
cnMaxDebugLen = 200;
Long lChannelFBTimeLineID = 1001;
Long lChannelFBTimes[] = {100000};
nUpdateTimeQty = 1;

cnPress = 1
cnPressRelease = 2

Char cnKeyDown[] = {'keyDown'}
Char cnKeyUp[] = {'keyUp'}
Char cnKeyPress[] = {'keyPress'}

//http://STBIP:port/dvr/getPlayList?[start=num][&max=num][&type=string][&clientAddr=string] 
//http://STBIP:port/remote/processKey?key=2&hold=keyPress[&clientAddr=string] 
//http://STBIP:port/serial/processCommand?cm=hex[&clientAddr=string]
//http://STBIP:port/tv/tune?major=202&minor=65535[&clientAddr=string] 
//http://STBIP:port/tv/getProgInfo?major=num[&minor=num][&time=num][&clientAddr=string] 
//http://STBIP:port/tv/getTuned?[&clientAddr=string] 

cnPort = 8080

Integer nChannels[]=
{
    1,   // 1- cnPlay
    2,   // 2- cnStop
    3,   // 3- cnPause
    4,   // 4- cnScanFwd
    5,   // 5- cnScanRev
    8,   // 6- cnRecord
    9,   // 7- cnPowerToggle
    10,  // 8- cn0
    11,  // 9- cn1
    12,  // 10- cn2
    13,  // 11- cn3
    14,  // 12- cn4
    15,  // 13- cn5
    16,  // 14- cn6
    17,  // 15- cn7
    18,  // 16- cn8
    19,  // 17- cn9
    20,  // 18- cnDash
    21,  // 19- cnEnter          
    22,  // 20- cnChanUp
    23,  // 21- cnChanDn
    27,  // 22- cnPowerOn
    28,  // 23- cnPowerOff
    30,  // 24- cnJump
    42,  // 25- cnBack
    45,  // 26- cnUp
    46,  // 27- cnDown
    47,  // 28- cnLeft
    48,  // 29- cnRight
    49,  // 30- cnSelect
    51,  // 31- cnExit
    56,  // 32- cnGuide
    57,  // 33- cnReplay
    60,  // 34- cnRed
    61,  // 35- cnGreen
    62,  // 36- cnYellow
    63,  // 37- cnBlue
    64,  // 38- cnAdvance
    65,  // 39- cnMenu
    66,  // 40- cnList
    67,  // 41- cnInfo
    54,  // 42- cnActive
    99  // 43- cnFormat
}           

(***********************************************************)
(*              DATA TYPE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_TYPE
Structure _sCommands
{
    Char strCommand[10]
    Integer nType
}

(***********************************************************)
(*               VARIABLE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_VARIABLE
Volatile _sCommands sCommands[43]

Volatile Char strCommandBuffer[20][10]
Volatile Char strKeyModeBuffer[20][10]

Volatile Char strInputBuffer[2000]
Volatile Char strChannelFBBuffer[200]

Volatile Char strJunk[20];

Volatile Integer nSerialCommandLen;

Volatile Char strClientAddress[40]

Volatile Integer nCurrentChannel
Volatile Integer nCurrentSubChannel
Volatile Char strCurrentChannelName[35]
Volatile Char strCurrentTitle[100]

(***********************************************************)
(*               LATCHING DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_LATCHING

(***********************************************************)
(*       MUTUALLY EXCLUSIVE DEFINITIONS GO BELOW           *)
(***********************************************************)
DEFINE_MUTUALLY_EXCLUSIVE

(***********************************************************)
(*        SUBROUTINE/FUNCTION DEFINITIONS GO BELOW         *)
(***********************************************************)
(* EXAMPLE: DEFINE_FUNCTION <RETURN_TYPE> <NAME> (<PARAMETERS>) *)
(* EXAMPLE: DEFINE_CALL '<NAME>' (<PARAMETERS>) *)

Define_Call 'DEBUG_OUTPUT' (Integer nLine, Char strData[])
{
    #IF_DEFINED DEBUG
    Char strTempData[200];
    Integer nLen;
	
    nLen = Length_String(strData)
    If (nLen < cnMaxDebugLen)
    {
	Send_String 0,"__FILE__,' Line:',Itoa(nLine),' ',strData";
    }
    Else
    {
	strTempData = Left_String(strData,cnMaxDebugLen);
	strData = Right_String(strData,nLen-cnMaxDebugLen);
	Send_String 0,"__FILE__,' Line:',Itoa(nLine),' ',strTempData";
	Send_String 0,"'Continued: ',strData";
    }
    #END_IF
}

Define_Call 'SEND_CMD' (Char strKeyMode[], Char strCommand[])
{
    Local_Var Integer nLen, nI
    nLen = Length_Array(strCommandBuffer) + 1
    If (nLen < Max_Length_Array(strCommandBuffer))
    {
	Call 'DEBUG_OUTPUT'(__LINE__,"'nLen',Itoa(nLen),' strKeyMode:',strKeyMode,' strCommand:',strCommand")
	Set_Length_Array(strCommandBuffer, nLen)
	Set_Length_Array(strKeyModeBuffer, nLen)
	Call 'DEBUG_OUTPUT'(__LINE__,"'new len',Itoa(Length_Array(strCommandBuffer))")
	strCommandBuffer[nLen] = strCommand
	Call 'DEBUG_OUTPUT'(__LINE__,"'strCommand:',strCommand")
	strKeyModeBuffer[nLen] = strKeyMode
    }
    Else // buffer full
    {
	Call 'DEBUG_OUTPUT'(__LINE__,"'Buffer Full: nLen',Itoa(nLen),' strKeyMode:',strKeyMode,' strCommand:',strCommand")
	nLen = Max_Length_Array(strCommandBuffer)
	For (nI = nLen; nI > 1; nI--)
	{
	    strCommandBuffer[nI-1] = strCommandBuffer[nI]
	    strKeyModeBuffer[nI-1] = strKeyModeBuffer[nI]
	}
	strCommandBuffer[nLen] = strCommand
	strKeyModeBuffer[nLen] = strKeyMode
    }
    IP_Client_Open(dvDTV_Rcvr.Port, strIPAddress, cnPort, IP_TCP)
}

Define_Call 'SEND_CMD_FROM_BUFFER'
{
	Local_Var Integer nLen, nI
	If (Length_Array(strCommandBuffer))
	{
	    Switch (strKeyModeBuffer[1])
	    {
		Case 'XCH':
		{
		    Call 'DEBUG_OUTPUT' (__LINE__,"'GET /tv/tune?major=',strCommandBuffer[1],'&minor=65535',strClientAddress,' HTTP/1.1',13,10,13,10")
		    Send_String dvDTV_Rcvr,"'GET /tv/tune?major=',strCommandBuffer[1],'&minor=65535',strClientAddress,' HTTP/1.1',13,10,13,10"
		}
		Case 'GETTUNED':
		{
		    Cancel_Wait 'GETTUNED'
		    Call 'DEBUG_OUTPUT' (__LINE__,"'GET /tv/getTuned?',strClientAddress,' HTTP/1.1',13,10,13,10")
		    Send_String dvDTV_Rcvr,"'GET /tv/getTuned',strClientAddress,' HTTP/1.1',13,10,13,10"
		}
		Case 'LIST':
		{
		    Call 'DEBUG_OUTPUT' (__LINE__,"'GET /dvr/getPlayList?',strClientAddress,' HTTP/1.1',13,10,13,10")
		    Send_String dvDTV_Rcvr,"'GET /dvr/getPlayList?',strClientAddress,' HTTP/1.1',13,10,13,10"
		}
		Default:
		{
		    Call 'DEBUG_OUTPUT' (__LINE__,"'GET /remote/processKey?key=',strCommandBuffer[1],'&hold=',strKeyModeBuffer[1],strClientAddress,' HTTP/1.1',13,10,13,10")
		    Send_String dvDTV_Rcvr,"'GET /remote/processKey?key=',strCommandBuffer[1],'&hold=',strKeyModeBuffer[1],strClientAddress,' HTTP/1.1',13,10,13,10"
		}
	    }

	    nLen = Length_Array(strCommandBuffer)
	    Call 'DEBUG_OUTPUT' (__LINE__,"'nLen=',Itoa(nLen)")
	    For (nI = nLen; nI > 1; nI--)
	    {
		strCommandBuffer[nI-1] = strCommandBuffer[nI]
		strKeyModeBuffer[nI-1] = strKeyModeBuffer[nI]
	    }
	    Set_Length_Array(strCommandBuffer, nLen-1)
	    Set_Length_Array(strKeyModeBuffer, nLen-1)
	}
	Else
	{
	    Call 'DEBUG_OUTPUT' (__LINE__,"'Buffer was empty but we connected anyway.'")
	}
}

Define_Function CHAR[100] GET_IP_ERROR (LONG lERR)
{
    Switch (lERR)
    {
	Case 0:
	    Return "";
	Case 2:
	    Return "'IP ERROR (',ITOA(lERR),'): General Failure (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Case 4:
	    Return "'IP ERROR (',ITOA(lERR),'): unknown host (IP_CLIENT_OPEN)'";
	Case 6:
	    Return "'IP ERROR (',ITOA(lERR),'): connection refused (IP_CLIENT_OPEN)'";
	Case 7:
	    Return "'IP ERROR (',ITOA(lERR),'): connection timed out (IP_CLIENT_OPEN)'";
	Case 8:
	    Return "'IP ERROR (',ITOA(lERR),'): unknown connection error (IP_CLIENT_OPEN)'";
	Case 9:
	    Return "'IP ERROR (',ITOA(lERR),'): Already closed (IP_CLIENT_CLOSE/IP_SERVER_CLOSE)'";
	Case 10:
	    Return "'IP ERROR (',ITOA(lERR),'): Binding error (IP_SERVER_OPEN)'";
	Case 11:
	    Return "'IP ERROR (',ITOA(lERR),'): Listening error (IP_SERVER_OPEN)'";
	Case 14:
	    Return "'IP ERROR (',ITOA(lERR),'): local port already used (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Case 15:
	    Return "'IP ERROR (',ITOA(lERR),'): UDP socket already listening (IP_SERVER_OPEN)'";
	Case 16:
	    Return "'IP ERROR (',ITOA(lERR),'): too many open sockets (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Default:
	    Return "'IP ERROR (',ITOA(lERR),'): Unknown'";
    }
}

(***********************************************************)
(*                STARTUP CODE GOES BELOW                  *)
(***********************************************************)
DEFINE_START

strClientAddress = ''

sCommands[1].strCommand = 'play'	// Play
sCommands[1].nType = cnPress
sCommands[2].strCommand = 'stop'	// Stop
sCommands[2].nType = cnPress
sCommands[3].strCommand = 'pause'	// Pause
sCommands[3].nType = cnPress
sCommands[4].strCommand = 'ffwd'	// ScanFwd
sCommands[4].nType = cnPressRelease
sCommands[5].strCommand = 'rew'		// ScanRev
sCommands[5].nType = cnPressRelease
sCommands[6].strCommand = 'record'	// Record
sCommands[6].nType = cnPress
sCommands[7].strCommand = 'power'	// PowerToggle
sCommands[7].nType = cnPress
sCommands[8].strCommand = '0'		// 0
sCommands[8].nType = cnPress
sCommands[9].strCommand = '1'		// 1
sCommands[9].nType = cnPress
sCommands[10].strCommand = '2'		// 2
sCommands[10].nType = cnPress
sCommands[11].strCommand = '3'		// 3
sCommands[11].nType = cnPress
sCommands[12].strCommand = '4'		// 4
sCommands[12].nType = cnPress
sCommands[13].strCommand = '5'		// 5
sCommands[13].nType = cnPress
sCommands[14].strCommand = '6'		// 6
sCommands[14].nType = cnPress
sCommands[15].strCommand = '7'		// 7
sCommands[15].nType = cnPress
sCommands[16].strCommand = '8'		// 8
sCommands[16].nType = cnPress
sCommands[17].strCommand = '9'		// 9
sCommands[17].nType = cnPress
sCommands[18].strCommand = 'dash'//	Dash
sCommands[18].nType = cnPress
sCommands[19].strCommand = 'enter'//	Enter
sCommands[19].nType = cnPress
sCommands[20].strCommand = 'chanup'//	ChanUp
sCommands[20].nType = cnPressRelease
sCommands[21].strCommand = 'chandown'//  ChanDn
sCommands[21].nType = cnPressRelease
sCommands[22].strCommand = 'poweron'//   PowerOn
sCommands[22].nType = cnPress
sCommands[23].strCommand = 'poweroff'//  PowerOff
sCommands[23].nType = cnPress
sCommands[24].strCommand = 'prev'//	Jump
sCommands[24].nType = cnPress
sCommands[25].strCommand = 'back'//	Back
sCommands[25].nType = cnPress
sCommands[26].strCommand = 'up'//	Up
sCommands[26].nType = cnPressRelease
sCommands[27].strCommand = 'down'//	Down
sCommands[27].nType = cnPressRelease
sCommands[28].strCommand = 'left'//	Left
sCommands[28].nType = cnPressRelease
sCommands[29].strCommand = 'right'//	Right
sCommands[29].nType = cnPressRelease
sCommands[30].strCommand = 'select'//	Select
sCommands[30].nType = cnPress
sCommands[31].strCommand = 'exit'//	Exit
sCommands[31].nType = cnPress
sCommands[32].strCommand = 'guide'//	Guide
sCommands[32].nType = cnPress
sCommands[33].strCommand = 'replay'//	Replay
sCommands[33].nType = cnPress
sCommands[34].strCommand = 'red'//	Red
sCommands[34].nType = cnPress
sCommands[35].strCommand = 'green'//	Green
sCommands[35].nType = cnPress
sCommands[36].strCommand = 'yellow'//	Yellow
sCommands[36].nType = cnPress
sCommands[37].strCommand = 'blue'//	Blue
sCommands[37].nType = cnPress
sCommands[38].strCommand = 'advance'//	Advance
sCommands[38].nType = cnPress
sCommands[39].strCommand = 'menu'//	Menu
sCommands[39].nType = cnPress
sCommands[40].strCommand = 'list'//	List
sCommands[40].nType = cnPress
sCommands[41].strCommand = 'info'//	Info
sCommands[41].nType = cnPress
sCommands[42].strCommand = 'active'//  Active
sCommands[42].nType = cnPress
sCommands[43].strCommand = 'format'//  Format
sCommands[43].nType = cnPress
         
nSerialCommandLen = 43

Set_Length_Array(strCommandBuffer,0)

Wait 500
    Timeline_Create(lChannelFBTimeLineID,lChannelFBTimes,1,Timeline_Relative,Timeline_Repeat)
(***********************************************************)
(*                THE EVENTS GO BELOW                      *)
(***********************************************************)
DEFINE_EVENT

Timeline_Event[lChannelFBTimeLineID]
{
    Cancel_Wait 'GETTUNED'
    Call 'SEND_CMD' ('GETTUNED','')
}

Data_Event[vdvDTV_Rcvr]
{
    Command:
    {
	Call 'DEBUG_OUTPUT' (__Line__,"'Command:',Data.Text")
	If (Find_String(Data.Text,'CLIENT_ADDRESS=',1))
	{
	    strJunk = Remove_String(Data.Text,'CLIENT_ADDRESS=',1)
	    strClientAddress = "'&clientAddr=',Data.Text"
	}
	Else IF (Find_String(Data.Text,'XCH',1))
	{
	    strJunk = Remove_String(Data.Text,'XCH',1)
	    Call 'SEND_CMD' ('XCH',Data.Text)
	    Wait 40 'GETTUNED'
		Call 'SEND_CMD' ('GETTUNED','')
	}
	Else IF (Find_String(Data.Text,'GETTUNED',1))
	{
	    Cancel_Wait 'GETTUNED'
	    Call 'SEND_CMD' ('GETTUNED','')
	}
    }
}

Data_Event[dvDTV_Rcvr]
{
    Online:
    {
	Call 'SEND_CMD_FROM_BUFFER'
    }
    OnError:
    {
	//If (GET_IP_ERROR(Data.Number) == )
	//    Call 'SEND_CMD_FROM_BUFFER'
	//Else
	//{
	    strInputBuffer=""
	    Call 'DEBUG_OUTPUT' (__LINE__, "GET_IP_ERROR(Data.Number)");
	//}
    }
    Offline:
    {
	If (Length_Array(strCOmmandBuffer) > 0)
	{
	    Call 'DEBUG_OUTPUT' (__LINE__,"'Offline: Length_Array(strCOmmandBuffer)',Itoa(Length_Array(strCOmmandBuffer))")
	    IP_Client_Open(dvDTV_Rcvr.Port, strIPAddress, cnPort, IP_TCP)
	}
    }
    String:
    {
	Local_Var Integer nTempChannel, nTempSubChannel, nBegin, nEnd
	Local_Var Char strTempChannelName[35], strTempTitle[100]
	
	nTempChannel = 0
	nTempSubChannel = 0
	strTempChannelName = ""
	
	Call 'DEBUG_OUTPUT' (__LINE__,"'Data.Text:',Data.Text")
	strInputBuffer = "strInputBuffer, Data.Text"
	While (Find_String (strInputBuffer,"$0A",1))
	{
	    strChannelFBBuffer = Remove_String (strInputBuffer,"$0A",1)
	    If (Find_String (strChannelFBBuffer,"'"callsign":'",1))
	    {
		strJunk = Remove_String (strChannelFBBuffer,"'"callsign":'",1)
		nBegin = Find_String (strChannelFBBuffer,"'"'",1)
		nBegin++
		nEnd = Find_String (strChannelFBBuffer,"'"'",nBegin)
		Call 'DEBUG_OUTPUT' (__LINE__,"'Callsign nBegin:',Itoa(nBegin),' Callsign End:',Itoa(nEnd)")
		strTempChannelName = Mid_String(strChannelFBBuffer,nBegin,nEnd-nBegin)
		Call 'DEBUG_OUTPUT' (__LINE__,"'strTempChannelName:',strTempChannelName")
	    }
	    If (Find_String (strChannelFBBuffer,"'"major":'",1))
	    {
		strJunk = Remove_String (strChannelFBBuffer,"'"major":'",1)
		nTempChannel = Atoi(strChannelFBBuffer)
		Call 'DEBUG_OUTPUT' (__LINE__,"'nTempChannel:',Itoa(nTempChannel)")
	    }
	    If (Find_String (strChannelFBBuffer,"'"minor":'",1))
	    {
		strJunk = Remove_String (strChannelFBBuffer,"'"minor":'",1)
		nTempSubChannel = Atoi(strChannelFBBuffer)
		Call 'DEBUG_OUTPUT' (__LINE__,"'nTempSubChannel:',Itoa(nTempSubChannel)")
	    }
	    If (Find_String (strChannelFBBuffer,"'"title":'",1))
	    {
		strJunk = Remove_String (strChannelFBBuffer,"'"title":'",1)
		nBegin = Find_String (strChannelFBBuffer,"'"'",1)
		nBegin++
		nEnd = Find_String (strChannelFBBuffer,"'"'",nBegin)
		Call 'DEBUG_OUTPUT' (__LINE__,"'Title nBegin:',Itoa(nBegin),' Title End:',Itoa(nEnd)")
		strTempTitle = Mid_String(strChannelFBBuffer,nBegin,nEnd-nBegin)
		Call 'DEBUG_OUTPUT' (__LINE__,"'strTempTitle:',strTempTitle")
	    }
	}
	If (nTempChannel != 0 && nTempSubChannel != 65535)
	{
	    If (nTempChannel != nCurrentChannel || nTempSubChannel != nCurrentSubChannel)
	    {
		nCurrentChannel = nTempChannel
		nCurrentSubChannel = nTempSubChannel
		strCurrentChannelName = strTempChannelName
		Send_String vdvDTV_Rcvr,"'CHAN=',Itoa(nCurrentChannel),'.',Itoa(nCurrentSubChannel)"
		Send_String vdvDTV_Rcvr,"'CHANNAME=',strCurrentChannelName"
	    }
	}
	Else If (nTempChannel != 0)
	{
	    If (nTempChannel != nCurrentChannel)
	    {
		nCurrentChannel = nTempChannel
		nCurrentSubChannel = nTempSubChannel
		strCurrentChannelName = strTempChannelName
		Send_String vdvDTV_Rcvr,"'CHAN=',Itoa(nCurrentChannel)"
		Send_String vdvDTV_Rcvr,"'CHANNAME=',strCurrentChannelName"
	    }
	}
	If (Length_String (strTempTitle) > 0 && strTempTitle != strCurrentTitle)
	{
	    strCurrentTitle = strTempTitle
	    Send_String vdvDTV_Rcvr,"'TITLE=',strCurrentTitle"
	}
    }
}

Channel_Event[vdvDTV_Rcvr,nChannels]
{
    On:
    {
	Call 'DEBUG_OUTPUT' (__LINE__, "'Entered Channel On Event.  nChannels:',Itoa(Get_Last(nChannels))");
	Cancel_Wait 'GETTUNED'
	If (Get_Last(nChannels) <= nSerialCommandLen)
	{
	    If (sCommands[Get_Last(nChannels)].nType = cnPress)
	    {
		Call 'SEND_CMD' (cnKeyPress,sCommands[Get_Last(nChannels)].strCommand)
		Call 'DEBUG_OUTPUT' (__LINE__, "cnKeyPress,' sCommands[',Itoa(Get_Last(nChannels)),'].strCommand:',sCommands[Get_Last(nChannels)].strCommand")
		// RDM Testing
		//If (Get_Last(nChannels) == 40) // if the cmd was list then ask for the list
		//{
		//    Call 'SEND_CMD' ('LIST','')
		//}
	    }
	    Else If (sCommands[Get_Last(nChannels)].nType = cnPressRelease)
	    {
		Call 'SEND_CMD' (cnKeyDown,sCommands[Get_Last(nChannels)].strCommand)
		Call 'DEBUG_OUTPUT' (__LINE__, "cnKeyDown,' sCommands[',Itoa(Get_Last(nChannels)),'].strCommand:',sCommands[Get_Last(nChannels)].strCommand")
	    }
	}
    }
    Off:
    {
	//Call 'DEBUG_OUTPUT' (__LINE__, "'Entered Channel Off Event.  nChannels:',Itoa(Get_Last(nChannels))");
	If (Get_Last(nChannels) <= nSerialCommandLen)
	{
	    If (sCommands[Get_Last(nChannels)].nType = cnPressRelease)
	    {
		Call 'SEND_CMD' (cnKeyUp,sCommands[Get_Last(nChannels)].strCommand)
		Call 'DEBUG_OUTPUT' (__LINE__, "cnKeyUp,' sCommands[',Itoa(Get_Last(nChannels)),'].strCommand:',sCommands[Get_Last(nChannels)].strCommand")
	    }
	}
	Wait 40 'GETTUNED'
	    Call 'SEND_CMD' ('GETTUNED','')
    }
}

(***********************************************************)
(*            THE ACTUAL PROGRAM GOES BELOW                *)
(***********************************************************)
DEFINE_PROGRAM

(***********************************************************)
(*                     END OF PROGRAM                      *)
(*        DO NOT PUT ANY CODE BELOW THIS COMMENT           *)
(***********************************************************)